/* Includes ------------------------------------------------------------------*/
#include "soft_iic.h"


/**与单片机相关*/
void xSDA_IN(IIC_t* iic)
{
    iic->sda_base->GDIR &= ~(1UL << iic->sda_pin);
}

void xSDA_OUT(IIC_t* iic)
{
    iic->sda_base->GDIR |= (1UL << iic->sda_pin);
}

void xIIC_SCL(IIC_t* iic,const char x)
{
    GPIO_PinWrite(iic->scl_base,iic->scl_pin,x);
}

void xIIC_SDA(IIC_t* iic,const char x)
{
    GPIO_PinWrite(iic->sda_base,iic->sda_pin,x);
}

uint8_t xREAD_SDA(IIC_t* iic)
{
    return GPIO_PinRead(iic->sda_base,iic->sda_pin);
}


/**
 * @brief  iic delay
 * @param  m: time for delay, 1 represent nearly 1us
 * @retval void
 */
void IIC_Delay(uint8_t m)
{
    volatile uint8_t i = 0;
    while (m--)
    {
        i = IIC_Delay_Time;
        while (i--)
            ;
    }
}

/**
 * @brief  iic initialization
 * @param  hiic: iic handler
 * @retval void
 */
void IIC_Init(IIC_t* iic)
{
    /* SCL High */
    xIIC_SCL(iic,1);

    /* SDA High */
    xIIC_SDA(iic,1);
}

/**
 * @brief  iic start signal
 * @param  hiic: iic handler
 * @retval void
 */
void IIC_Start(IIC_t* iic)
{
    /* SDA Output */
    xSDA_OUT(iic);
    xIIC_SDA(iic,1);
    xIIC_SCL(iic,1);
    IIC_Delay(1);

    /* START: when CLK is high,DATA change form high to low */
    xIIC_SDA(iic,0);
    IIC_Delay(1);

    /* Clamp I2C bus, prepare to send or receive data */
    xIIC_SCL(iic,0);
}

/**
 * @brief  iic stop signal
 * @param  hiic: iic handler
 * @retval void
 */
void IIC_Stop(IIC_t* iic)
{
    /* SDA Output */
    xSDA_OUT(iic);
    xIIC_SCL(iic,0);

    /* STOP: when CLK is high DATA change form low to high */
    xIIC_SDA(iic,0);
    IIC_Delay(1);
    xIIC_SCL(iic,1);
    IIC_Delay(1);

    /* Send I2C bus stop signal */
    xIIC_SDA(iic,1);
}

/**
 * @brief  iic wait ack signal
 * @param  hiic: iic handler
 * @retval 1,fail
 *         0,success
 */
unsigned char IIC_Wait_Ack(IIC_t* iic)
{
    unsigned char ucErrTime = 0;

    /* SDA Input */
    xSDA_IN(iic);
    xIIC_SDA(iic,1);

    IIC_Delay(1);
    xIIC_SCL(iic,1);
    IIC_Delay(1);

    while (xREAD_SDA(iic))
    {
        ucErrTime++;
        if (ucErrTime > 250)
        {
            IIC_Stop(iic);
            return 1;
        }
    }
    xIIC_SCL(iic,0);
    return 0;
}

/**
 * @brief  iic produce ack signal
 * @param  hiic: iic handler
 * @retval void
 */
void IIC_Ack(IIC_t* iic)
{
    xIIC_SCL(iic,0);
    xSDA_OUT(iic);
    xIIC_SDA(iic,0);
    IIC_Delay(1);
    xIIC_SCL(iic,1);
    IIC_Delay(1);
    xIIC_SCL(iic,0);
}

/**
 * @brief  iic produce no ack signal
 * @param  hiic: iic handler
 * @retval void
 */
void IIC_NAck(IIC_t* iic)
{
    xIIC_SCL(iic,0);
    xSDA_OUT(iic);
    xIIC_SDA(iic,1);
    IIC_Delay(1);
    xIIC_SCL(iic,1);
    IIC_Delay(1);
    xIIC_SCL(iic,0);
}

/**
 * @brief  iic send a byte
 * @param  hiic: iic handler
 * @param  txd: the byte to be sent
 * @retval void
 */
void IIC_Send_Byte(IIC_t* iic,unsigned char txd)
{
    unsigned char t;
    xSDA_OUT(iic);
    xIIC_SCL(iic,0);
    for (t = 0; t < 8; t++)
    {
        xIIC_SDA(iic,(txd & 0x80) >> 7);
        txd <<= 1;

        /* All three delays are necessary for TEA5767 */
        IIC_Delay(1);
        xIIC_SCL(iic,1);
        IIC_Delay(1);
        xIIC_SCL(iic,0);
        IIC_Delay(1);
    }
}

/**
 * @brief  iic read a byte
 * @param  hiic: iic handler
 * @param  ack: 0,send no ack signal
 *              1,send ack signal
 * @retval void
 */
unsigned char IIC_Read_Byte(IIC_t* iic,unsigned char ack)
{
    unsigned char i, receive = 0;

    /* SDA Input */
    xSDA_IN(iic);

    for (i = 0; i < 8; i++)
    {
        xIIC_SCL(iic,0);
        IIC_Delay(1);
        xIIC_SCL(iic,1);
        receive <<= 1;
        if (xREAD_SDA(iic))
            receive++;
        IIC_Delay(1);
    }
    if (!ack)
    {
        /* send no ack signal */
        IIC_NAck(iic);
    }
    else
    {
        /* send ack signal */
        IIC_Ack(iic);
    }
    /* Return the byte received */
    return receive;
}

/**
 * @brief  iic write a byte to the register of the device
 * @param  hiic: iic handler
 * @param  addr: the address of the device
 * @param  reg: the address of the register(offset address)
 * @param  data: the byte to be sent
 * @retval 0,success
 *         1,fail
 */
unsigned char IIC_Device_Write_Byte(IIC_t* iic,unsigned char addr, unsigned char reg, unsigned char data)
{
    IIC_Start(iic);

    /* send the address of the device and the write command */
    IIC_Send_Byte(iic,(addr << 1) | 0);
    if (IIC_Wait_Ack(iic))
    {
        IIC_Stop(iic);
        return 1;
    }
    /* send the address of the register */
    IIC_Send_Byte(iic,reg);
    IIC_Wait_Ack(iic);

    /* send the data to the register */
    IIC_Send_Byte(iic,data);
    if (IIC_Wait_Ack(iic))
    {
        IIC_Stop(iic);
        return 1;
    }
    IIC_Stop(iic);
    return 0;
}

/**
 * @brief  iic read a byte of the register of the device
 * @param  hiic: iic handler
 * @param  addr: the address of the device
 * @param  reg: the address of the register(offset address)
 * @retval the data read
 */
unsigned char IIC_Device_Read_Byte(IIC_t* iic,unsigned char addr, unsigned char reg)
{
    unsigned char res;
    IIC_Start(iic);

    /* send the address of the device and the write command */
    IIC_Send_Byte(iic,(addr << 1) | 0);
    IIC_Wait_Ack(iic);

    /* send the address of the register */
    IIC_Send_Byte(iic,reg);
    IIC_Wait_Ack(iic);
    IIC_Start(iic);

    /* send the address of the device and the read command */
    IIC_Send_Byte(iic,(addr << 1) | 1);
    IIC_Wait_Ack(iic);

    /* read data and send no ack signal */
    res = IIC_Read_Byte(iic,0);

    /* send a stop signal */
    IIC_Stop(iic);
    return res;
}

/**
 * @brief  iic continuous write to the register of the device
 * @param  hiic: iic handler
 * @param  addr: the address of the device
 * @param  reg: the address of the register(offset address)
 * @param  len: write length
 * @param  buf: data buffer
 * @retval 0,success
 *         1,fail
 */
unsigned char IIC_Device_Write_Len(IIC_t* iic,unsigned char addr, unsigned char reg, unsigned char len, unsigned char *buf)
{
    unsigned char i;
    IIC_Start(iic);

    /* send the address of the device and the write command */
    IIC_Send_Byte(iic,(addr << 1) | 0);
    if (IIC_Wait_Ack(iic))
    {
        IIC_Stop(iic);
        return 1;
    }

    /* send the address of the register */
    IIC_Send_Byte(iic,reg);
    IIC_Wait_Ack(iic);
    for (i = 0; i < len; i++)
    {
        /* send the data to the register */
        IIC_Send_Byte(iic,buf[i]);
        if (IIC_Wait_Ack(iic))
        {
            IIC_Stop(iic);
            return 1;
        }
    }
    IIC_Stop(iic);
    return 0;
}

/**
 * @brief  iic continuous read of the register of the device
 * @param  hiic: iic handler
 * @param  addr: the address of the device
 * @param  reg: the address of the register(offset address)
 * @param  len: read length
 * @param  buf: data buffer
 * @retval 0,success
 *         1,fail
 */
unsigned char IIC_Device_Read_Len(IIC_t* iic,unsigned char addr, unsigned char reg, unsigned char len, unsigned char *buf)
{
    IIC_Start(iic);

    /* send the address of the device and the write command */
    IIC_Send_Byte(iic,(addr << 1) | 0);
    if (IIC_Wait_Ack(iic))
    {
        IIC_Stop(iic);
        return 1;
    }

    /* send the address of the register */
    IIC_Send_Byte(iic,reg);
    IIC_Wait_Ack(iic);
    IIC_Start(iic);

    /* send the address of the device and the read command */
    IIC_Send_Byte(iic,(addr << 1) | 1);
    IIC_Wait_Ack(iic);
    while (len)
    {
        /* end of the reading, send no ack signal */
        if (len == 1)
            *buf = IIC_Read_Byte(iic,0);
        else
            *buf = IIC_Read_Byte(iic,1);
        len--;
        buf++;
    }
    /* produce a stop signal */
    IIC_Stop(iic);
    return 0;
}

uint8_t SCCB_Device_Read(IIC_t* iic,unsigned char addr, unsigned char reg)
{
    uint8_t temp;
    IIC_Start(iic);
    IIC_Send_Byte(iic,addr<<1);
    IIC_Wait_Ack(iic);
    IIC_Send_Byte(iic,reg);
    IIC_Wait_Ack(iic);
    IIC_Stop(iic);

    IIC_Start(iic);
    IIC_Send_Byte(iic,(addr<<1)|0x01);
    IIC_Wait_Ack(iic);
    temp = IIC_Read_Byte(iic,0);
    IIC_Stop(iic);
    return temp;
}

uint16_t SCCB_Device_Read16bit(IIC_t* iic,unsigned char addr, unsigned char reg)
{
    uint16_t temp;
    IIC_Start(iic);
    IIC_Send_Byte(iic,addr<<1);
    IIC_Wait_Ack(iic);
    IIC_Send_Byte(iic,reg);
    IIC_Wait_Ack(iic);
    IIC_Stop(iic);

    IIC_Start(iic);
    IIC_Send_Byte(iic,(addr<<1)|0x01);
    IIC_Wait_Ack(iic);
    temp = ((uint16_t)IIC_Read_Byte(iic,1))<<8;
    temp |= IIC_Read_Byte(iic,0);
    IIC_Stop(iic);
    return temp;
}

uint8_t SCCB_Device_Write(IIC_t* iic,unsigned char addr, unsigned char reg,unsigned char data)
{
     IIC_Start(iic);

    /* send the address of the device and the write command */
    IIC_Send_Byte(iic,(addr << 1) | 0);
    if (IIC_Wait_Ack(iic))
    {
        IIC_Stop(iic);
        return 1;
    }
    /* send the address of the register */
    IIC_Send_Byte(iic,reg);
    IIC_Wait_Ack(iic);

    /* send the data to the register */
    IIC_Send_Byte(iic,data);
    if (IIC_Wait_Ack(iic))
    {
        IIC_Stop(iic);
        return 1;
    }
    IIC_Stop(iic);
    return 0;
}

uint8_t SCCB_Device_Write16bit(IIC_t* iic,unsigned char addr, unsigned char reg,uint16_t data)
{
     IIC_Start(iic);

    /* send the address of the device and the write command */
    IIC_Send_Byte(iic,(addr << 1) | 0);
    if (IIC_Wait_Ack(iic))
    {
        IIC_Stop(iic);
        return 1;
    }
    /* send the address of the register */
    IIC_Send_Byte(iic,reg);
    IIC_Wait_Ack(iic);

    /* send the data to the register */
    IIC_Send_Byte(iic,(data&0xFF00)>>8);
    if (IIC_Wait_Ack(iic))
    {
        IIC_Stop(iic);
        return 1;
    }
		IIC_Send_Byte(iic,(data&0x00FF));
		if (IIC_Wait_Ack(iic))
    {
        IIC_Stop(iic);
        return 1;
    }
    IIC_Stop(iic);
    return 0;
}